package simmar.neat;

import ch.idsia.agents.Agent;
import ch.idsia.agents.controllers.BasicMarioAIAgent;
import ch.idsia.benchmark.mario.engine.sprites.Mario;
import ch.idsia.benchmark.mario.environments.Environment;

import com.anji.integration.Activator;

import simmar.input.InputKnowledge;
import simmar.input.InputLibrary;

public class NEATAgent extends BasicMarioAIAgent implements Agent
{
	// Object storing current situation and remembered knowledge. Can be queried for network input
	protected InputKnowledge knowledge;
	
	// The activator holding the neural network
	private Activator agentActivator;
	
	// Stimuli array. 2d because the activator expects multiple stimuli arrays per query, but we only care about one.
	private double[][] stimuli;
	private static final int stimuliSize = 6;
	
	/**
	 * Dummy constructor. this one won't work as no activator is given
	 */
	public NEATAgent() {
	    super("NEATAgent");
	    reset();
	}
	
	/**
	 * primary constructor. sets the activator supplied
	 * @param newActivator
	 */
	public NEATAgent(Activator newActivator) {
		super("NEATAgent");
		this.agentActivator = newActivator;
	    reset();
	}

	/**
	 * Reset function
	 */
	public void reset()
	{
		// Reset action array
	    action = new boolean[Environment.numberOfKeys];
	    
	    // Reset stimuli array
	    stimuli = new double[1][stimuliSize];
	    
	    // reset knowledge
	    if (knowledge != null) {
			knowledge.reset();
		} else {
			knowledge = new InputKnowledge();
		}
	}
	
	/**
	 * Integrate observations. Inherited
	 */
	public void integrateObservation(Environment environment)
	{
		knowledge.update(environment);
		
		// Collect input
		if (environment != null && knowledge != null) {
			stimuli[0][0] = InputLibrary.getBinaryCanAscend(knowledge, environment);
			stimuli[0][1] = InputLibrary.getBinaryObstacleAhead(knowledge, environment);
			stimuli[0][2] = InputLibrary.getBinaryPitAhead(knowledge, environment);
			stimuli[0][3] = InputLibrary.getBinaryDangerBelow(knowledge, environment);
			stimuli[0][4] = InputLibrary.getBinaryShotAimed(knowledge, environment);
			stimuli[0][5] = InputLibrary.getBinaryEnemyAhead(knowledge, environment);
		}
	}

	/**
	 * Activate network and return action. Inherited
	 */
	public boolean[] getAction()
	{
	    // this Agent requires observation integrated in advance.

	    // ACTIVATOR CALL HERE
	    double[][] response;
	    
	    // First sanity check activator presence and input
	    if (agentActivator != null) {
	    	if (stimuli != null && stimuli.length == 1 && stimuli[0].length == agentActivator.getInputDimension()) {
	    		response = agentActivator.next(stimuli);
	    		
	    		// Convert response to action
	    		for (int i = 0; i < response[0].length; i++) {
	    			// Check if response is above threshold for true
	    			if (response[0][i] >= 0.9) {
	    				action[i] = true;
	    			} else {
	    				action[i] = false;
	    			}
	    		}
	    		
	    	} else {
	    		System.out.println("NEATAgent: Input dimensions invalid!");
	    	}
	    } else {
	    	System.out.println("NEATAgent: No activator set!");
	    }
	    
	    if (action[3]) {
	    	knowledge.jumpStart();
	    }

	    return action;
	}
	
};